-- The MIT License
-- 
-- Copyright (c) 2008 Matthew Maravillas, Patrick Quinn
-- 
-- Permission is hereby granted, free of charge, to any person obtaining a copy
-- of this software and associated documentation files (the "Software"), to deal
-- in the Software without restriction, including without limitation the rights
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-- copies of the Software, and to permit persons to whom the Software is
-- furnished to do so, subject to the following conditions:
-- 
-- The above copyright notice and this permission notice shall be included in
-- all copies or substantial portions of the Software.
-- 
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-- THE SOFTWARE.

local Class = import('/lua/system/class.lua').Class
local mapUtils = import('/mods/koth/hook/lua/MapUtilities.lua')
local Flag = import('/mods/koth/hook/lua/flag.lua').Flag
local util = import('/mods/koth/hook/lua/util.lua')

local HILL_STATE = { 
    Empty = 0, 
    Claimed = 1, 
    Contested = 2,
}

Hill = Class(){
    __init = function(self, radius, armies, unitPresenceFunc)
    -- unitPresenceFunc takes one argument, the unit, and returns a boolean
    -- based on whether that unit should count towards presence on the hill.
    -- For instance, a wall unit may not count towards an army's presence,
    -- while an engineer might.

        self.radius = radius
        self.armies = armies
        self.IsUnitPresent = unitPresenceFunc

        -- Set the position
        self.position = {}
        self.position.x, self.position.y, self.position.z = mapUtils.GetMapCenter()

        for index, army in pairs(armies) do
            local flag = Flag(army, index, table.getn(self.armies), self.position)
        end

        LOG('Created ' .. radius .. ' unit radius hill for these armies: ' .. repr(self.armies))
        
        -- Create the circle graphic for the hill
        self.hillDecal = CreateDecal(Vector(self.position.x, self.position.y, self.position.z), 0, '/mods/KotH/decals/circle.dds', '', 'Albedo', 2*radius, 2*radius, 100000, 0, -1, 0)
--        self.hillSplat = CreateSplat(Vector(self.position.x, self.position.y, self.position.z), 0, '/mods/KotH/decals/circle.dds', 2*self.radius, 2*self.radius, 100000, 0, -1, 0)
        
        self.lastSeenArmies = {}
        
        self.hillState = HILL_STATE.Empty
        self.hillOpen = false

        util.CreateEvent(self, 'HillContested')
        util.CreateEvent(self, 'HillClaimed')
        util.CreateEvent(self, 'HillAbandoned')
        
        util.CreateEvent(self, 'ArmyArrived')
        util.CreateEvent(self, 'ArmyDeparted')
        
        util.CreateEvent(self, 'HillOpened')
        
        self:AddArmyArrivedCallback(function(army)
            local brain = GetArmyBrain(army)
            
            if brain.hasFlag then
                brain:GetFlag():Activate()
            end
        end)
        
        self:AddArmyDepartedCallback(function(army)
            local brain = GetArmyBrain(army)
            
            if brain.hasFlag then
                brain:GetFlag():Deactivate()
            end
        end)
    end,
    
    IsArmyPresent = function(self, army)
    -- army is an army brain, returns true if the army is present on the hill
        local radius = self.radius
        local position = self.position
        local x1 = position.x - radius
        local y1 = position.y - radius
        local z1 = position.z - radius
        local x2 = position.x + radius
        local y2 = position.y + radius
        local z2 = position.z + radius
        local unitsInRec = GetUnitsInRect( Rect(x1, z1, x2, z2) )
        
        #Check for empty rectangle
        if not unitsInRec then
            return unitsInRec
        end

        for k, v in unitsInRec do
           if army:GetArmyIndex() == v:GetArmy() and self.IsUnitPresent(v) then
                local dist = VDist3({position.x, position.y, position.z}, v:GetPosition())
                if dist <= radius then
                    return true
                end   
            end
        end
        return false
    end,

    GetPresentArmies = function(self, getCached)
        if not getCached or not self.cachedPresentArmies then
        -- This is not the most efficient function, since it calls IsArmyPresent once per army
        -- It can be cleaned up if performance is a problem
            self.cachedPresentArmies = {}
            
            for armyIndex, army in self.armies do
                if self:IsArmyPresent(GetArmyBrain(army)) then
                    table.insert(self.cachedPresentArmies, army)
                end
            end
        end
        
        return self.cachedPresentArmies
    end,
    
    IsContested = function(self)
        return table.getn(self:GetPresentArmies(true)) > 1
    end,
    
    IsClaimed = function(self)
        return table.getn(self:GetPresentArmies(true)) == 1
    end,
    
    IsEmpty = function(self)
        return table.getn(self:GetPresentArmies(true)) == 0
    end,
    
    IsOpen = function(self)
        return self.hillOpen
    end,
    
    UpdateStatus = function(self)
        if not self:IsOpen() then
            return {}
        end
        
        local armies = self:GetPresentArmies()
        
        local departedArmies, arrivedArmies, unchangedArmies = util.arrayDifference(self.lastSeenArmies, armies)
        
        for i, army in departedArmies do
            LOG('UpdateStatus: ' .. army .. ' is no longer on the hill')
            self:ArmyDeparted(army)
        end
           
        for i, army in arrivedArmies do
            LOG('UpdateStatus: ' .. army .. ' arrived on the hill')
            self:ArmyArrived(army)
        end

        if table.getn(departedArmies) ~= 0 or table.getn(arrivedArmies) ~= 0 then
            LOG('UpdateStatus: Armies on the hill have changed')
            
            if self:IsClaimed() and self.hillState != HILL_STATE.Claimed then
                LOG('UpdateStatus: Hill claimed')
                
                self.hillState = HILL_STATE.Claimed
                self:HillClaimed(armies[1])
            
            elseif self:IsContested() and self.hillState != HILL_STATE.Contested then
                LOG('UpdateStatus: Hill contested')
                
                self.hillState = HILL_STATE.Contested
                self:HillContested()
            
            elseif self:IsEmpty() and self.hillState != HILL_STATE.Empty then
                LOG('UpdateStatus: Hill abandoned')
                
                self.hillState = HILL_STATE.Empty
                self:HillAbandoned()
            end
        end

        Sync.ArmiesOnHill = armies
        
        if self:IsClaimed() then
            Sync.ArmiesClaimingHill = {armies[1],}
        else
            Sync.ArmiesClaimingHill = {}
        end
        
        self.lastSeenArmies = armies
        
        return armies
    end,
    
    Open = function(self)
        LOG('Open: Hill opened')
        self.hillOpen = true
        self:HillOpened()
        -- self.hillDecal:Destroy()
        self.hillDecal:Destroy()
        self.hillDecal = CreateDecal(Vector(self.position.x, self.position.y, self.position.z), 0, '/mods/KotH/decals/limecircle.dds', '', 'Glow', 2*self.radius, 2*self.radius, 100000, 0, -1, 0)
        -- self.hillSplat = CreateSplat(Vector(self.position.x, self.position.y, self.position.z), 0, '/mods/KotH/decals/limecircle.dds', 2*self.radius, 2*self.radius, 100000, 0, -1, 0)
    end,
}